package com.cy.security;

import java.util.Calendar;
import java.util.Date;

public class Encryption {
    //åŠ å¯†è¿‡ç¨‹ï¼ˆè¾“å…¥ï¼šRAWã€OSã€KEYï¼Œè¾“å‡ºï¼šMARKã€CSï¼‰ï¼Œæ•°æ®éƒ½ä»¥åè¿›åˆ¶å­—ç¬¦ä¸²æ ¼å¼
    /**
     * è¾“å…¥é¡?
     * raw:	å¾…åŠ å¯†æ˜Žæ–‡ï¼Œ2ä¸ªå­—èŠ‚ï¼Œ5ä½åè¿›åˆ¶å­—ç¬¦ä¸²ï¼ˆå·¦ç«¯0å¡«å……ï¼‰ï¼Œæœ‰æ•ˆå€¼ï¼š00000~65535
     * os:	æ“ä½œå‡­è¯ï¼Œtimestampï¼?ä¸ªå­—èŠ‚ï¼Œ10ä½åè¿›åˆ¶å­—ç¬¦ä¸²ï¼ˆå·¦ç«¯0å¡«å……ï¼‰ï¼Œæ—¶é—´æˆ?
     * key:	æ“ä½œå¯†é’¥ï¼?ä¸ªå­—èŠ‚ï¼Œ5ä½åè¿›åˆ¶å­—ç¬¦ä¸²ï¼ˆå·¦ç«¯0å¡«å……ï¼‰ï¼Œæœ‰æ•ˆå€¼ï¼š00000~99999
     */
    public static String ver = "2015.04.25";
    private String raw;
    private String os;
    private String key;
    private String dn;
    private String tn;
    private static int startYear = 2014;
    public static long startOs;
    public static Date osDate;
    private static char [] hashList = { 'p', 'K', '9', 'B', 'g', 'J', 'v', '3', '6', 'U', 'N', 'k', '*', 'X', '!', 'D', 'b', '+', 'l', 'x', '_', 't', 'z', '`', 'C', ')', 'O', 'F', 'u', '%', 'I', '@', '5', 's', 'Z', 'y', 'n', 'j', 'r', 'e', '7', 'o', '$', 'a', 'S', 'H', 'G', 'W', '8', 'A', '2', '4', 'w', '1', '~', 'L', 'T', '(', 'M', 'E', 'f', '&', 'm', 'V', 'q', '^', 'R', 'Y', '-', 'i', '=', 'Q', 'h', 'c', 'P', 'd', '0'};

//        private String dn8;

    /**
     * mark:	å¯†æ–‡ï¼?ä¸ªå­—èŠ‚ï¼Œ10ä½åè¿›åˆ¶å­—ç¬¦ä¸²ï¼ˆå·¦ç«¯0å¡«å……ï¼?
     * cs:		æ ¡éªŒç ï¼Œ2ä¸ªå­—èŠ‚ï¼Œ5ä½åè¿›åˆ¶å­—ç¬¦ä¸²ï¼ˆå·¦ç«¯0å¡«å……ï¼?
     */
    private String mark;
    private String cs;

    public String getCode(){
        return tn+mark+cs+os;
    }

    public void setCode(String code){
        if((code==null) || code.length()<2)
            return;

        tn=code.substring(0,2);
        try{
            mark=code.substring(2,10);
            cs=code.substring(10,14);
            os=code.substring(14,22);
        }catch(Exception e){
            mark = "";
            cs = "";
            os = "";
        }
    }

    public String getRaw() {
        return raw;
    }


    public void setRaw(String raw) {
        this.raw = raw;
    }


    public String getOs() {
        return os;
    }


    public void setOs(String os) {
        this.os = os;
    }


    public String getKey() {
        return key;
    }


    public void setKey(String key) {
        this.key = key;
    }


    public String getDn() {
        return dn;
    }


    public void setDn(String dn) {
        this.dn = dn;
    }


    public String getTn() {
        return tn;
    }


    public void setTn(String tn) {
        this.tn = tn;
    }


    public String getMark() {
        return mark;
    }


    public void setMark(String mark) {
        this.mark = mark;
    }


    public String getCs() {
        return cs;
    }


    public void setCs(String cs) {
        this.cs = cs;
    }


    /**
     * encrypt: è®¾å¤‡åŠ å¯†ç®—æ³•
     * @param enc: éœ?®¾ç½®Encryptionä¸­çš„raw, os, key
     * @returnï¼šè¿”å›žEncryptionä¸­çš„cs, mark
     */
    public static Encryption encrypt(Encryption enc){

        // 1. deal with cs
        enc.cs = getCs(enc.raw, enc.key);

        // 2. deal with dn
        String [] dns = getDns(enc.dn, enc.tn);
        String dn30 = dns[0];
        String dn74 = dns[1];
        String dnD8 = dns[2];
        // 3. deal with shift
        int _nS = getShift(enc.cs, enc.os, enc.key, dnD8, dn74, dn30);

        // 4. deal with exp
        long _nRaw = Long.parseLong(enc.raw,16);
        String _raw2 = CommonUtil.int2Str((int) (_nRaw%256), 2, 8, "0");
        _nRaw /= 256;
        _raw2 = CommonUtil.int2Str((int) (_nRaw%256), 2, 8, "0")+_raw2;

        long _nCs = Long.parseLong(enc.cs, 16);
        String _cs2 = CommonUtil.int2Str((int) (_nCs%256), 2, 8, "0");
        _nCs /= 256;
        _cs2 = CommonUtil.int2Str((int) (_nCs%256), 2, 8, "0")+_cs2;

        String _exp2 = "";
        if(_nS%2 == 0){
            // insert cs bit in even position
            for(int i=0; i<16; i++){
                _exp2 = _cs2.charAt(i)+_exp2; //even pos
                _exp2 = _raw2.charAt(15-i)+_exp2;	//odd pos
            }
            //right shift, for example: 0x12345678 -> 0x78123456
            _exp2 = _exp2.substring(_exp2.length()-8*_nS,_exp2.length())+_exp2.substring(0, _exp2.length()-8*_nS);
        }else{
            // insert bit in odd position
            for(int i=0; i<16; i++){
                _exp2 = _raw2.charAt(15-i)+_exp2; 	//even pos
                _exp2 = _cs2.charAt(i)+_exp2;	//odd pos
            }
            //left shift, for example: 0x12345678 -> 0x34567812
            _exp2 = _exp2.substring(8*_nS, _exp2.length())+_exp2.substring(0, 8*_nS);
        }

        // 4. deal with nMark;
        long _nExp = (long)Long.parseLong(_exp2.substring(0, 8), 2)*256*256*256
                +(long)Long.parseLong(_exp2.substring(8, 16),2)*256*256
                +(long)Long.parseLong(_exp2.substring(16,24),2)*256
                +(long)Long.parseLong(_exp2.substring(24,32),2);
        long _nOs = (long)Long.parseLong(enc.os, 16);
        long _nKey_nCs = (long)Long.parseLong(enc.key, 16)*256*256+Long.parseLong(enc.cs, 16);
        long _nDn30 = (long)Long.parseLong(dn30, 16);
        long _nDn74 = (long)Long.parseLong(dn74, 16);
        long _nDnD8 = (long)Long.parseLong(dnD8, 16);
        long _nMark = _nExp^_nOs^_nKey_nCs^_nDn30^_nDn74^_nDnD8;

        // ä¸ºé˜²æ­¢integeræº¢å‡ºï¼Œå°†integerè½¬æˆä¸¤ç«¯å­—ç¬¦ä¸²æ‹¼æŽ?
        enc.mark = CommonUtil.int2Str((int)_nMark, 16, 8, "0").toUpperCase();

        return enc;
    }



    /**
     * decrypt_d: è®¾å¤‡è§£å¯†ç®—æ³•
     * @param enc: éœ?®¾ç½®Encryptionä¸­çš„mark, os, cs, key
     * @return: è¿”å›žEncryptionä¸­çš„raw
     */
    public static Encryption decrypt(Encryption enc){
        // 1. æ‰©å±•ç EXPï¼šç”±MARKä¸ŽOSã€[KEY,CS]å„ä½XORèŽ·å¾—
        long _nOs = (long)Long.parseLong(enc.os, 16);
        long _nKey_nCs = (long)Long.parseLong(enc.key, 16)*256*256+Long.parseLong(enc.cs, 16);
        long _nMark = (long)Long.parseLong(enc.mark,16);

        String [] dns = getDns(enc.dn, enc.tn);
        String dn30 = dns[0];
        String dn74 = dns[1];
        String dnD8 = dns[2];

        long _nDn30 = (long)Long.parseLong(dn30, 16);
        long _nDn74 = (long)Long.parseLong(dn74, 16);
        long _nDnD8 = (long)Long.parseLong(dnD8, 16);

        long _nExp = _nMark ^ _nOs ^ _nKey_nCs^_nDn30^_nDn74^_nDnD8;

        // 2. ç§»ä½ç Sï¼šå–æž„æˆCSã€OSã€KEYå„å­—èŠ‚ä¹‹å’Œæ¨¡2ï¼?
        Integer _nS = getShift(enc.cs, enc.os, enc.key, dnD8, dn74, dn30);
        String _raw2 = "";
        // ä¸ºé˜²æ­¢integeræº¢å‡ºï¼Œå°†integerè½¬æˆä¸¤ç«¯å­—ç¬¦ä¸²æ‹¼æŽ?
        String _exp2 = CommonUtil.int2Str((int) (_nExp%256), 2, 8, "0");
        _nExp /= 256;
        _exp2 = CommonUtil.int2Str((int) (_nExp%256), 2, 8, "0") + _exp2;
        _nExp /= 256;
        _exp2 = CommonUtil.int2Str((int) (_nExp%256), 2, 8, "0") + _exp2;
        _nExp /= 256;
        _exp2 = CommonUtil.int2Str((int) (_nExp%256), 2, 8, "0") + _exp2;

        if(_nS%2 == 0){
            // left shift, for example: 0x12345678 -> 0x34567812
            _exp2 = _exp2.substring(8*_nS, _exp2.length())+_exp2.substring(0, 8*_nS);

            //RAWç¬¬iä½å–EXP_tmpä¸­çš„ç¬?*iä½?
            for(int i=0; i<16; i++){
                _raw2 = _raw2 + _exp2.charAt(2*i);
            }
        }else{
            // right shift, for example: 0x12345678 -> 0x78123456
            _exp2 = _exp2.substring(_exp2.length()-8*_nS, _exp2.length())+_exp2.substring(0,_exp2.length()-8*_nS);
            //åˆ™RAWç¬¬iä½å–EXP_tmpçš„ç¬¬2*i+1ä½ã?
            for(int i=0; i<16; i++){
                _raw2 =  _raw2 + _exp2.charAt(2*i+1);
            }
        }

        //3. deal with raw
        enc.raw = CommonUtil.int2Str(Integer.parseInt(_raw2, 2), 16, 4, "0").toUpperCase();
        enc.cs = getCs(enc.raw, enc.key);
        return enc;
    }

    /**
     * getCs: èŽ·å–æ ¡éªŒç ç®—æ³?
     * @param raw: æºç 
     * @param key: æ“ä½œå¯†é’¥
     * @return: è¿”å›žcsæ ¡éªŒç ï¼Œ5ä½åè¿›åˆ¶æ•?
     */
    public static String getCs(String raw, String key){
        int _nSize = 16;

        long _nRaw = Long.parseLong(raw, 16);
        String _key2 = CommonUtil.int2Str(Integer.parseInt(key, 16), 2, 16, "0");

        String _res2 = "";
        for(int i=0; i<_nSize; i++){
            int _nR = ((int) ((_nRaw/(i+1)) % 2)==1 ^ _key2.charAt(15-i)=='1')?1:0;
            _res2 = _nR + _res2;
        }

        return CommonUtil.int2Str(Integer.parseInt(_res2, 2), 16, 4, "0").toUpperCase();
    }

    /**
     * getShift: èŽ·å–ç§»ä½æ ‡å¿—S
     * @param cs: æ ¡éªŒç ?
     * @param os: æ—¶é—´æˆ?
     * @param key: æ“ä½œç§˜é’¥
     * @return: è¿”å›žç§»ä½æ ‡å¿—S
     */
    private static int getShift (String cs, String os, String key, String dnD8, String dn74, String dn30){
        int _nS = 0;
        int _nCs = Integer.parseInt(cs, 16);
        int _nKey = Integer.parseInt(key, 16);
//            int _nTn = Integer.parseInt(tn, 16);
        long _nOs = Long.parseLong(os,16);

        _nS += (_nCs%256);
        _nCs /= 256;
        _nS += (_nCs%256);

        _nS += (_nKey%256);
        _nKey /= 256;
        _nS += (_nKey%256);

        _nS += (_nOs%256);
        _nOs /= 256;
        _nS += (_nOs%256);
        _nOs /= 256;
        _nS += (_nOs%256);
        _nOs /= 256;
        _nS += (_nOs%256);

        for(int i=0; i<4; i++){
            _nS += Integer.parseInt(dnD8.substring(i*2, i*2+2), 16);
            _nS += Integer.parseInt(dn74.substring(i*2, i*2+2), 16);
            _nS += Integer.parseInt(dn30.substring(i*2, i*2+2), 16);
        }
//            _nS += _nTn;
        _nS %= 4;

        return _nS;
    }

    private static String[] getDns(String dn, String tn){
        //split dn into two halves
        dn = dn.toUpperCase().trim();
        dn = dn.substring(Math.max(dn.length()-24, 0), dn.length());
        int len = dn.length();
        for(int i=0; i<24-len;i++){
            dn = "0"+dn;
        }

        String dns[] = new String[3];
        dns[0] = dn.substring(dn.length()-8, dn.length());
        dns[1] = dn.substring(dn.length()-16, dn.length()-8);
        dns[2] = dn.substring(0, dn.length()-16);

        if(len<=14){
            //stc chips have only 7 bytes cpu_id.
            //need to fix highest byte of dns by adding tn.
            if(tn.length()!=2)
                tn = "00";
            dns[1] = tn + dns[1].substring(2);
        }

        return dns;
    }

//        private int checkSum(String strs){
//
//        	int checksum = 0;
//
//        	if(strs.length()%2==1)
//        		strs="0"+strs;
//
//        	for(int i=0; i<strs.length()/2; i++){
//        		checksum += Integer.parseInt(strs.substring(i*2, i*2+2),16);
//        	}
//
//        	return checksum;
//
//        }

    /***
     * 根据一个数字成生Hash字符串
     * @param code
     * @param len
     * @return
     */
    public static String getHashCode(int code, int len){
        int base = hashList.length;

        code = (code>0?code:-code);
        String result = "";
        for(int i = 0; i<len; i++){
            result = hashList[code%base] + result;
            code/=base;
        }
        return result;
    }

    /**
     * 将Hash字符转成id
     * @param hash
     * @return
     */
    public static int getHash2UserId(String hash){
        int base = hashList.length;

        String hash_code = hash.trim();
        int userId = 0;
        int code_size = hash_code.length();
        for(int i=0; i<code_size; i++){
            int index = 0;
            for(; index<base; index++){
                if(hashList[index]==hash_code.charAt(i))
                    break;
            }
            if(index==base){
                return userId = -1;
            }
            userId*=base;
            userId+=index;
        }

        return userId;
    }

    public static String getDateOs(int ... timeArray){
        Calendar c = Calendar.getInstance();
//            c.set(2014,1,1);
        c.setTime(new Date());
        int year = c.get(Calendar.YEAR);//获取年份
        int month=c.get(Calendar.MONTH);//获取月份
        int day=c.get(Calendar.DATE);//获取日
        int hour=c.get(Calendar.HOUR_OF_DAY);//小时
        int minute=c.get(Calendar.MINUTE);//分
        int second=c.get(Calendar.SECOND);//秒
        month++;
        for(int i = 0; i < timeArray.length ;i++){
            switch (i){
                case 0 :
                    year += timeArray[i];
                    break;
                case 1 :
                    month += timeArray[i];
                    break;
                case 2 :
                    day += timeArray[i];
                    break;
                case 3 :
                    hour += timeArray[i];
                    break;
                case 4 :
                    minute += timeArray[i];
                    break;

            }
        }
        c.set(year,month-1,day,hour,minute,second);
        osDate = c.getTime();

//            (年份-2014)*455+月份*35+日期
        int sumDay = (year-startYear)*455+month*35+day;
//            时*3600+分*60+秒
        int sumSecond = hour*3600+minute*60+second;

        String strSecond = "00000"+sumSecond;
        strSecond = strSecond.substring(strSecond.length()-5,strSecond.length());
        strSecond = sumDay + strSecond;
        startOs = Long.parseLong(strSecond);

        String  hexOs = CommonUtil.int2Str( startOs, 16, 8, "0");

        return hexOs.toUpperCase();
    }

}
